package com.silence.gmall.util;

import cn.idev.excel.ExcelWriter;
import cn.idev.excel.FastExcelFactory;
import cn.idev.excel.write.builder.ExcelWriterSheetBuilder;
import cn.idev.excel.write.metadata.WriteSheet;
import cn.idev.excel.write.style.AbstractCellStyleStrategy;
import com.silence.gmall.base.BaseExcelBean;
import com.silence.gmall.base.BaseExcelErrorMessage;
import com.silence.gmall.listener.AbstractEasyExcelListener;
import jakarta.servlet.http.HttpServletResponse;
import lombok.*;

import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author silence
 * @Email 792245022@qq.com
 * @Date 2025-06-11 14:50
 */
@ToString
@EqualsAndHashCode
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class EasyExcelUtil {

    /**
     * 读取excel
     *
     * @param inputStream 输入流
     * @return 读取 builder
     */
    public static ReadBuilder readExcel(InputStream inputStream) {
        return new ReadBuilder(inputStream);
    }

    /**
     * 写入excel
     *
     * @param response response 响应
     * @param fileName 文件名
     * @return 写出 builder
     * @throws IOException 异常
     */
    public static WriteBuilder writeExcel(HttpServletResponse response, String fileName) throws IOException {
        return new WriteBuilder(response, fileName);
    }

    /**
     * 通过模板写出 Excel
     *
     * @param response         response 响应
     * @param fileName         文件名
     * @param templateFileName 模板文件名
     * @param clazz            累
     * @param data             数据
     * @param <T>              泛型
     * @throws IOException 异常
     */
    public static <T> void writeExcelWithTemplate(HttpServletResponse response, String fileName,
                                                  String templateFileName, Class<T> clazz, List<T> data) throws IOException {
        response.setContentType("application/vnd.ms-excel;charset=UTF-8");
        response.setHeader("content-disposition", "attachment; filename=" +
                URLEncoder.encode(fileName + ".xlsx", StandardCharsets.UTF_8));
        FastExcelFactory.write(response.getOutputStream(), clazz)
                .withTemplate(templateFileName).
                sheet()
                .doWrite(data);
    }

    /**
     * 写出 builder
     */
    public static class WriteBuilder {

        /**
         * excel 写出器
         */
        ExcelWriter excelWriter;

        /**
         * 写出 builder
         *
         * @param response response 响应
         * @param fileName 文件名
         * @throws IOException 异常
         */
        WriteBuilder(HttpServletResponse response, String fileName) throws IOException {
            response.setContentType("application/vnd.ms-excel;charset=UTF-8");
            response.setHeader("content-disposition", "attachment; filename=" +
                    URLEncoder.encode(fileName + ".xlsx", StandardCharsets.UTF_8));
            this.excelWriter = FastExcelFactory.write(response.getOutputStream()).build();
        }

        /**
         * 写出 sheet 页
         *
         * @param sheetNo             页号
         * @param sheetName           页名
         * @param clazz               实体类
         * @param data                数据
         * @param cellStyleStrategies 单元格策略
         * @param <T>                 泛型
         * @return 写出 builder
         */
        public <T> WriteBuilder writeSheet(Integer sheetNo, String sheetName,
                                           Class<T> clazz, List<T> data, AbstractCellStyleStrategy... cellStyleStrategies) {
            ExcelWriterSheetBuilder writerSheetBuilder = FastExcelFactory.writerSheet(sheetNo - 1, sheetName);
            for (AbstractCellStyleStrategy styleStrategy : cellStyleStrategies) {
                writerSheetBuilder.registerWriteHandler(styleStrategy);
            }
            WriteSheet writeSheet = writerSheetBuilder.head(clazz).build();
            this.excelWriter.write(data, writeSheet);
            return this;
        }

        /**
         * 结束写出
         */
        public void finish() {
            this.excelWriter.finish();
        }
    }

    /**
     * 读取 builder
     */
    public static class ReadBuilder {

        /**
         * 输入流
         */
        private final InputStream inputStream;

        /**
         * 获取容器
         */
        private final Map<Integer, Container<? extends BaseExcelBean>> containers = new HashMap<>();

        /**
         * 读取 builder
         *
         * @param inputStream 输入流
         */
        ReadBuilder(InputStream inputStream) {
            this.inputStream = inputStream;
        }

        /**
         * 读取 sheet 页
         *
         * @param sheetNo        页号
         * @param clazz          实体类
         * @param uploadListener 上传监听器
         * @param <T>            泛型
         * @return 读取 builder
         */
        public <T extends BaseExcelBean> ReadBuilder readSheet(Integer sheetNo, Class<T> clazz,
                                                               AbstractEasyExcelListener<T> uploadListener) {
            Container<T> container = new Container<>();
            container.setClazz(clazz);
            container.setBaseListener(uploadListener);
            this.containers.put(sheetNo, container);
            return this;
        }

        /**
         * 读取 sheet 页
         *
         * @param sheetNo        页号
         * @param clazz          实体类
         * @param uploadListener 上传监听器
         * @param headRowNumber  表头行号
         * @param <T>            泛型
         * @return 读取 builder
         */
        public <T extends BaseExcelBean> ReadBuilder readSheet(Integer sheetNo, Class<T> clazz,
                                                               AbstractEasyExcelListener<T> uploadListener,
                                                               Integer headRowNumber) {
            Container<T> container = new Container<>();
            container.setClazz(clazz);
            container.setBaseListener(uploadListener);
            container.setHeadRowNumber(headRowNumber);
            this.containers.put(sheetNo, container);
            return this;
        }

        /**
         * 开始读取
         *
         * @return 读取结果
         */
        public List<BaseExcelErrorMessage> doRead() {
            List<BaseExcelErrorMessage> errList = new ArrayList<>();
            for (Map.Entry<Integer, Container<? extends BaseExcelBean>> integerContainerEntry : this.containers.entrySet()) {
                Integer key = integerContainerEntry.getKey();
                Container<? extends BaseExcelBean> container = integerContainerEntry.getValue();
                FastExcelFactory.read(this.inputStream, container.getClazz(), container.getBaseListener())
                        .sheet(key - 1)
                        .headRowNumber(container.getHeadRowNumber() - 1)
                        .doRead();
                List<BaseExcelErrorMessage> list = container.getBaseListener().getErrList();
                errList.addAll(list);
            }
            return errList;
        }

        /**
         * 容器
         *
         * @param <T> 泛型
         */
        @Data
        private static class Container<T extends BaseExcelBean> {

            /**
             * 实体类
             */
            private Class<T> clazz;

            /**
             * 上传监听器
             */
            private AbstractEasyExcelListener<T> baseListener;

            /**
             * 表头行号
             */
            private Integer headRowNumber = 2;

        }
    }
}
